#ifndef _HOOK_H_
#error "This file must be included only from Hook.h"
#endif

#include <stdarg.h>
#include <boost/mpl/if.hpp>

#include <boost/type_traits/is_reference.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/type_traits/is_fundamental.hpp>



#include "HookProcManager.h"

#define BOOST_SIGNALS_NO_LIB

#include <boost/function_types/function_type.hpp>
//#include <boost/transformation/push_front.hpp>



namespace sd
{
namespace hp
{

template <typename TupleTypeIterator, typename TupleTypeIteratorEnd >
inline void LoadParam ( va_list& args, TupleTypeIterator & it, TupleTypeIteratorEnd& it_end )
{
	typedef typename boost::fusion::result_of::deref<TupleTypeIterator>::type ParamType;
	typedef typename boost::remove_reference< ParamType >::type ParamTypeNoRef;

	*it = va_arg( args, ParamTypeNoRef );
	LoadParam( args, boost::fusion::next( it ), it_end );
}


template <typename TupleTypeIteratorEnd >
inline void LoadParam ( va_list &, TupleTypeIteratorEnd &, TupleTypeIteratorEnd & ){}


template <typename TupleType>
inline void LoadParams (va_list& args, TupleType& params )
{
	LoadParam( args, boost::fusion::begin( params ), boost::fusion::end( params ) );
}






template< typename StackVar >
struct StackSizeImpl
{
	enum{ Align = 4 };
	enum{ result = ( ( sizeof( StackVar ) - 1 ) / Align + 1 ) * Align };
};

template< typename StackVar >
struct StackSize
{
	enum{ result = 
		boost::mpl::if_c< boost::is_pointer<StackVar>::value || boost::is_fundamental<StackVar>::value, 
			StackSizeImpl<StackVar>, 
			boost::mpl::if_c< boost::is_reference<StackVar>::value, 
				StackSizeImpl< boost::add_pointer< boost::remove_reference<StackVar>::type >::type >, 
				StackSizeImpl< boost::add_pointer< StackVar > > 
			>::type 
		>::type::result };
};


template< typename TupleBegin, typename TupleEnd >
struct CalcParamsStackSizeImpl
{
	enum{ result = StackSize< boost::fusion::result_of::deref<TupleBegin>::type >::result + CalcParamsStackSizeImpl< boost::fusion::result_of::next<TupleBegin>::type , TupleEnd >::result };
};

template< typename TupleEnd >
struct CalcParamsStackSizeImpl<TupleEnd,TupleEnd>
{
	enum{ result = 0 };
};


template< typename TupleName >
struct CalcParamsStackSize
{
	enum{ result = CalcParamsStackSizeImpl< boost::fusion::result_of::begin<TupleName>::type, boost::fusion::result_of::end<TupleName>::type >::result };
};



template<  typename TProc >
void* __cdecl Hook<TProc>::innerCallback( void* userData, int* workedParams,
	void* /*reserv1*/, void* reserv2, ... )
{
	Hook<TProc>* self = static_cast< Hook<TProc>* >( userData );

	va_list args;
	va_start(args, reserv2);

	ParamList params;
	LoadParams( args, params );

	*workedParams = CalcParamsStackSize< ParamList >::result;

	ResultType rv;

	tools::locker< HookBase > locker(*self);
	rv = self->callback_( *self, params );

	return (void*)rv;
}



template<  typename TProc >
void   Hook<TProc>::setDummyActive(bool active)
{
	tools::locker< HookBase > locker(*this);
	if(active)
	{
		store_callback_	= callback_;
		callback_= OriginalCaller(*this);
	}else
		callback_= store_callback_;
}


}// namespace hp
}// namespace sd
